use std::boxed::Box;

#[derive(Debug)]
struct Foo;

#[test]
fn trait_test() {
    println!("foo is {:?}", Foo);
}

#[test]
fn impl_trait_test() {
    trait HasArea {
        fn area(&self) -> i32;
    }

    struct Rect {
        x: i32,
        y: i32,
    }
    impl HasArea for Rect {
        fn area(&self) -> i32 {
            self.x * self.y
        }
    }

    //before
    fn _foo() -> Box<dyn HasArea> {
        Box::new(Rect { x: 2, y: 3 }) as Box<dyn HasArea>
    }
    // the return type of a function must have a statically known size [E0277]
    // fn foobar() -> Trait {
    //     5 as Trait
    // }

    //after
    fn _foo2() -> impl HasArea {
        Rect { x: 3, y: 4 }
    }

    // http://wiki.jikexueyuan.com/project/rust-primer/trait/trait.html
    // 闭包
    {
        // before
        fn _foo() -> Box<dyn Fn(i32) -> i32> {
            Box::new(|x| x + 1)
        }

        // after
        fn _foo2() -> impl Fn(i32) -> i32 {
            |x| x + 1
        }
    }
    // 传参
    {
        // before
        fn get_area<T: HasArea>(x: T) -> i32 {
            x.area()
        }
        // after
        fn _get_area2(x: impl HasArea) -> i32 {
            x.area()
        }

        let r = Rect { x: 5, y: 9 };
        assert_eq!(45, get_area(r));
    }
}

#[test]
fn trait_object_test() {
    trait MyTrait {
        fn my_method(&self) -> String;
    }

    impl MyTrait for u8 {
        fn my_method(&self) -> String {
            format!("{} for u8", self)
        }
    }

    impl MyTrait for String {
        fn my_method(&self) -> String {
            format!("{} for string", self)
        }
    }

    fn foo(mt: &dyn MyTrait) {
        println!("ret =  {:?}", mt.my_method());
    }

    foo(&"hello".to_string());
}
